﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using LongNet.Util;

namespace LongNet.Config
{
    /// <summary>
    /// A simple thread-safe file watcher class.
    /// </summary>
    public class FileWatcher : IDisposable
    {
        #region Fields

        private static LongNet.Util.LogManager.Logger logger = LongNet.Util.LogManager.GetCurrentClassLogger();
        private FileSystemWatcher watcher;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the path of the file to watch.
        /// </summary>
        public string FilePath
        {
            get;
            set;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of <see cref="FileWatcher"/>.
        /// </summary>
        public FileWatcher()
        {
        }

        /// <summary>
        /// Creates a new instance of <see cref="FileWatcher"/>.
        /// </summary>
        /// <param name="path">The path of the file to watch.</param>
        public FileWatcher(string path)
        {
            FilePath = path;
        }

        /// <summary>
        /// Creates a new instance of <see cref="FileWatcher"/>.
        /// </summary>
        /// <param name="path">The path of the file to watch.</param>
        /// <param name="onChange">The FileSystemEventHandler raised when the file is created,changed or deleted.</param>
        public FileWatcher(string path, FileSystemEventHandler onChange)
        {
            FilePath = path;
            this.OnChange += onChange;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Occurs when the file is created,changed or deleted.
        /// </summary>
        public event FileSystemEventHandler OnChange;

        /// <summary>
        /// Disposes the watcher.
        /// </summary>
        public void Dispose()
        {
            this.StopWatching();
        }

        /// <summary>
        /// Stops watching file.
        /// </summary>
        public void StopWatching()
        {
            lock (this)
            {
                if (this.watcher != null)
                {
                    if (logger.IsDebugEnabled)
                        logger.Debug("Stop watching file: {0}", FilePath);

                    this.watcher.EnableRaisingEvents = false;
                    this.watcher.Dispose();
                    this.watcher = null;
                }
            }
        }

        /// <summary>
        /// Starts watching file.
        /// </summary>
        public void StartWatching()
        {
            lock (this)
            {
                if (this.watcher == null)
                {
                    if (logger.IsDebugEnabled)
                        logger.Debug("Start watching file: {0}", FilePath);

                    this.watcher = new FileSystemWatcher();
                    this.watcher.Path = Path.GetDirectoryName(FilePath);
                    this.watcher.Filter = Path.GetFileName(FilePath);
                    this.watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime | NotifyFilters.Size | NotifyFilters.Security | NotifyFilters.Attributes;
                    this.watcher.Created += new FileSystemEventHandler(this.OnChanged);
                    this.watcher.Changed += new FileSystemEventHandler(this.OnChanged);
                    this.watcher.Deleted += new FileSystemEventHandler(this.OnChanged);
                    this.watcher.EnableRaisingEvents = true;
                }
                else if (this.watcher.EnableRaisingEvents == false)
                {
                    if (logger.IsDebugEnabled)
                        logger.Info("Start watching file: {0}", FilePath);

                    this.watcher.EnableRaisingEvents = true;
                }
            }
        }

        private void OnChanged(object source, FileSystemEventArgs e)
        {
            lock (this)
            {
                if (this.OnChange != null)
                {
                    if (logger.IsDebugEnabled)
                        logger.Debug("OnChange raised, file: {0}", FilePath);

                    this.watcher.EnableRaisingEvents = false;
                    this.OnChange(source, e);
                    if (this.watcher != null && this.watcher.EnableRaisingEvents == false)
                        this.watcher.EnableRaisingEvents = true;
                }
            }
        }

        #endregion
    }
}
